package com.dkd.findmyfriends;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.support.v4.app.NavUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;

public class PlayGameActivity extends Activity implements OnClickListener {
//	private static final String TAG = PlayGameActivity.class.getSimpleName();
	private static final Random r = new Random(); 

	private static final int FLIP_OVER_TIMEOUT = 1000;
	
	private ImageCardView guessOne = null;
	private ImageCardView guessTwo = null;
	private boolean boardLocked = false;
	
	private int totalGuesses = 0;
	private int correctGuesses = 0;
	private int totalImages = 0;
	private boolean scoreEligible = false;
	private long startTime = -1;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_play_game);

		int rotation = getWindowManager().getDefaultDisplay().getRotation();
		if(rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
			GameSettings.setLandscape(true);
		} else {
			GameSettings.setLandscape(false);
		}
		
		startGame();
	}
	
	private void startGame() {
		totalGuesses = 0;
		correctGuesses = 0;
		startTime = -1;
		setEndGameButtonsVisible(View.INVISIBLE);
		DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		int width = metrics.widthPixels; 
		int height = metrics.heightPixels - 50;
		int columnWidth = Math.min(width/GameSettings.getNumCols(), height/GameSettings.getNumRows());
		
		ArrayList<ImageCardView> cards = new ArrayList<ImageCardView>();
		
		if(GameSettings.imagesSelected == null || GameSettings.imagesSelected.size()==0) {
			totalImages = addRandomCards(cards, columnWidth);
		} else {
			totalImages = 0;
			for(int i = 0; i < GameSettings.imagesSelected.size(); i++) {
				Bitmap photo = GameSettings.imagesSelected.get(GameSettings.imagesSelected.keyAt(i));
				cards.add(new ImageCardView(this, totalImages, photo, false, getResources(), columnWidth));
				cards.add(new ImageCardView(this, totalImages++, photo, false, getResources(), columnWidth));
			}
		}
		
		scoreEligible = (totalImages == GameSettings.getMaxNumImages()); 
		if(!scoreEligible) {
			Random r = new Random();
			int[] colors = new int[columnWidth*columnWidth];
			
			while(totalImages < GameSettings.getMaxNumImages()) {
				int c = Color.argb(255, r.nextInt(255), r.nextInt(255), r.nextInt(255));
				for(int i = 0; i < colors.length; i++) {
					colors[i] = c;
				}
				Bitmap color = Bitmap.createBitmap(colors, columnWidth, columnWidth, Config.ARGB_4444);
				cards.add(new ImageCardView(this, totalImages, color, false, getResources(), columnWidth));
				cards.add(new ImageCardView(this, totalImages++, color, false, getResources(), columnWidth));
			}
			Toast.makeText(PlayGameActivity.this, "Score will not count because you do not have the max number of images for this grid size.", Toast.LENGTH_LONG).show();
		}

		Collections.shuffle(cards);
		Collections.shuffle(cards);
		
		GridView gridview = (GridView) findViewById(R.id.gridview);
	    gridview.setAdapter(new ImageAdapter(cards));
	    gridview.setColumnWidth(columnWidth);
	    gridview.setNumColumns(GameSettings.getNumCols());
//	    gridview.setStretchMode(GridView.STRETCH_SPACING);//TRY GridView.STRETCH_COLUMN_WIDTH
		
	    gridview.setOnItemClickListener(cardClickListnr);
	}
	
	private int addRandomCards(ArrayList<ImageCardView> cards, int columnWidth) {
		int counter = 0;
		Cursor cur = getContentResolver().query(ContactsContract.Data.CONTENT_URI, new String[]{Photo.PHOTO},
				Photo.PHOTO + " is not null AND " +
				ContactsContract.Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'", 
				null, ContactsContract.Contacts.DISPLAY_NAME);	
		if(GameSettings.getMaxNumImages() >= cur.getCount()) {
			//need more images than we have, add all of them
			while(cur.moveToNext()) {
				byte[] data = cur.getBlob(cur.getColumnIndex(Photo.PHOTO));
				Bitmap photo = BitmapFactory.decodeByteArray(data, 0, data.length);
				if(photo != null) {
					cards.add(new ImageCardView(this, counter, photo, false, getResources(), columnWidth));
					cards.add(new ImageCardView(this, counter++, photo, false, getResources(), columnWidth));
				}
			}
		} else {
			Set<Integer> randomUsed = new HashSet<Integer>();
			//have more images than we need, add random images
			int pos = r.nextInt(cur.getCount());
			randomUsed.add(pos);
			while(counter < GameSettings.getMaxNumImages() && cur.moveToPosition(pos)) {
				byte[] data = cur.getBlob(cur.getColumnIndex(Photo.PHOTO));
				Bitmap photo = BitmapFactory.decodeByteArray(data, 0, data.length);
				if(photo != null) {
					cards.add(new ImageCardView(this, counter, photo, false, getResources(), columnWidth));
					cards.add(new ImageCardView(this, counter++, photo, false, getResources(), columnWidth));
				}
				while(!randomUsed.add(pos)) {
					pos = r.nextInt(cur.getCount());
				}						
			}
		}
		return counter;
	}

	private OnItemClickListener cardClickListnr = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        	if(startTime == -1) {
        		startTime = System.currentTimeMillis();
        	}
        	GridView x = (GridView) parent;
        	ImageCardView c = (ImageCardView) x.getAdapter().getItem(position);
        	if(boardLocked || c.isMatched() || guessOne == c) {
        		return;
        	}
    		c.setFaceUp(true);
        	if(guessOne == null) {
        		guessOne = c;
        	} else {
        		totalGuesses++;
        		guessTwo = c;
        		if(guessOne.gameEquals(guessTwo)) {
        			guessOne.setMatched(true);
        			guessTwo.setMatched(true);
        			correctGuesses++;
        			if(correctGuesses == totalImages) {
        				long endTime = System.currentTimeMillis();
	        			setEndGameButtonsVisible(View.VISIBLE);
	        			prepareScoreDialog(scoreEligible, endTime - startTime);
        			}
        		} else {
        			new WaitToFlip().execute();
        		}
        		guessOne = null;
        		guessTwo = null;
        	}
        }
    };
	
	private void prepareScoreDialog(boolean eligibleScore, long elapsedTime) {
		if(eligibleScore) {
			SharedPreferences settings = getSharedPreferences(SPKeys.PREFS_FILE_NAME, MODE_PRIVATE);
			int fewestClicks = settings.getInt(SPKeys.FEWEST_CLICKS + GameSettings.getGridSize(), Integer.MAX_VALUE);
			long bestTime = settings.getLong(SPKeys.FASTEST_TIME + GameSettings.getGridSize(), Long.MAX_VALUE);
	
			boolean saveClicks = false;
			boolean saveTime = false; 
			if(totalGuesses < fewestClicks) {
				saveClicks = true;
			}
			if(elapsedTime < bestTime) {
				saveTime = true;
			}
			
			if(saveClicks || saveTime) {
				storeLocalScore(saveClicks, totalGuesses, saveTime, elapsedTime);
				return;
			}
		}
		
		AlertDialog d = showScoreDialog(false, null);
		//change score text
		TextView clicksText = (TextView) d.findViewById(R.id.clicksText);
		TextView timeText = (TextView) d.findViewById(R.id.timeText);
		clicksText.setText(clicksText.getText().toString() + totalGuesses);
		timeText.setText(timeText.getText().toString() + GameSettings.getTimeString(elapsedTime));
	}
	

	private void storeLocalScore(final boolean saveClicks, final int clicks, final boolean saveTime, final long time) {
		final AlertDialog d = showScoreDialog(true, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				EditText et = (EditText)((AlertDialog)dialog).findViewById(R.id.username);
				String name = et.getText().toString();
				if(name != null && !name.trim().equals("")) {
					SharedPreferences.Editor editor = getSharedPreferences(SPKeys.PREFS_FILE_NAME, MODE_PRIVATE).edit();
					if(saveClicks) {
						editor.putString(SPKeys.FEWEST_CLICKS + GameSettings.getGridSize() + SPKeys.NAME, name);
						editor.putInt(SPKeys.FEWEST_CLICKS + GameSettings.getGridSize(), clicks);
						saveScoreToGlobalDb(clicks, null, name);
					}
					if(saveTime) {
						editor.putString(SPKeys.FASTEST_TIME + GameSettings.getGridSize() + SPKeys.NAME, name);
						editor.putLong(SPKeys.FASTEST_TIME + GameSettings.getGridSize(), time);
						saveScoreToGlobalDb(null, time, name);
					}
					editor.commit();
				}

			}
		});
		//change score text
		TextView st = (TextView) d.findViewById(R.id.scoreText);
		st.setText("Score: " + clicks + " clicks in " + GameSettings.getTimeString(time));
		((EditText)d.findViewById(R.id.username)).addTextChangedListener(new TextWatcher() {
			public void onTextChanged(CharSequence s, int start, int before, int count) {}
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
			public void afterTextChanged(Editable s) {
				d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0);
			}
		});
	}

	private class WaitToFlip extends AsyncTask<Void, Void, Void> {
		private ImageCardView gOne = null;
		private ImageCardView gTwo = null;
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			boardLocked = true;
			gOne = guessOne;
			gTwo = guessTwo;			
		}

		@Override
		protected Void doInBackground(Void... params) {
			try {
				Thread.sleep(FLIP_OVER_TIMEOUT);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
			gOne.setFaceUp(false);
			gTwo.setFaceUp(false);
			
			boardLocked = false;

			gOne = null;
			gTwo = null;
		}		
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_play_game, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			NavUtils.navigateUpFromSameTask(this);
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	
	private AlertDialog showScoreDialog(boolean highScore, final DialogInterface.OnClickListener okList) {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder
			.setView(this.getLayoutInflater().inflate(highScore ? R.layout.hs_name_layout : R.layout.score_layout, null))
			.setPositiveButton(R.string.hs_ok, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
//					finish();
					if(okList != null)
						okList.onClick(dialog, which);
				}
			});
		AlertDialog d = builder.create();
		d.show();
		//Disable ok button to wait for username when they get a high score
		d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!highScore);
		if(highScore) {
			//open keyboard
			d.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
		}
		return d;
	}

	private void saveScoreToGlobalDb(Integer clicks, Long time, String username) {
		Database.insertScore(username, time, clicks, GameSettings.getGridSize());
	}

	private void setEndGameButtonsVisible(int visibility) {
		findViewById(R.id.viewHsBtn).setVisibility(visibility);
		findViewById(R.id.finishBtn).setVisibility(visibility);
		findViewById(R.id.replayBtn).setVisibility(visibility);
	}
	
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.viewHsBtn:
			startActivity(new Intent(this, HighScoresActivity.class));
			break;
		case R.id.finishBtn:
			finish();
			break;
		case R.id.replayBtn:
			startGame();
			break;
		}
		
	}

}
